//+---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation, 2008
//
// File: EventLogger.cs
//
// Contents: FrontEnd Common Event Logger singleton
//
// Created by: Andrew Lin (yunlin) July 22nd, 2008
//
//----------------------------------------------------------------------------

using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading;
using System.Reflection;

namespace Microsoft.Reputation.Services
{
    public static class CommonEventLogger
    {
        private const int DatabaseActionEventId = 0;
        private const int DatabaseProcessEventId = 1;

        public const int EventSucceedState = 1;
        public const int EventFailureState = 0;

        private static int s_DatabaseConnectionState = EventSucceedState;
        private static int s_DatabaseProcessState = EventSucceedState;

        private static bool s_eventLoggerActive;
        private static int s_eventIdBase = 200;
        private static int s_antiEventIdBase = 300;
        private static bool s_eventLoggerThrottleDisabled = false;

        public static EventLog EventLog { get; set; }

        public static void Initialize(string eventLoggerSource)
        {
            if (ServiceConfig.EventLoggerDisabled)
            {
                return;
            }

            s_eventLoggerThrottleDisabled = ServiceConfig.EventLoggerThrottleDisabled;

            EventLog = new EventLog();
            EventLog.Source = eventLoggerSource;

            s_eventLoggerActive = true;
        }

        public static void Shutdown()
        {
            if (CommonEventLogger.EventLog != null)
            {
                CommonEventLogger.EventLog.Dispose();
                CommonEventLogger.EventLog = null;
            }
        }

        public static void LogDatabaseActionSuccess()
        {
            LogSuccessEvent(
                ref s_DatabaseConnectionState,
                s_antiEventIdBase + DatabaseActionEventId);
        }

        public static void LogDatabaseActionFailure(string errorMsg)
        {
            LogFailureEvent(
                ref s_DatabaseConnectionState,
                s_eventIdBase + DatabaseActionEventId,
                errorMsg);
        }

        public static void LogDatabaseProcessSuccess()
        {
            LogSuccessEvent(
                ref s_DatabaseProcessState,
                s_antiEventIdBase + DatabaseProcessEventId);
        }

        public static void LogDatabaseProcessFailure(string errorMsg)
        {
            LogFailureEvent(
                ref s_DatabaseProcessState,
                s_eventIdBase + DatabaseProcessEventId,
                errorMsg);
        }

        public static void LogSuccessEvent(
            ref int eventState,
            int eventId)
        {
            try
            {
                //
                // Log the first success after the serial of failures, if throttling is turned on.
                //
                if (!s_eventLoggerThrottleDisabled &&
                    Interlocked.CompareExchange(
                        ref eventState,
                        EventSucceedState,
                        EventFailureState) == EventFailureState)
                {
                    if (s_eventLoggerActive)
                    {
                        lock (CommonEventLogger.EventLog)
                        {
                            //
                            // Write the success message to event log.
                            //
                            CommonEventLogger.EventLog.WriteEntry(
                                "The failure associated with the previous Error " + eventId + " has been resolved, and the operation succeeded.",
                                EventLogEntryType.Information,
                                eventId);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_eventLoggerActive = false;
            }
        }

        public static void LogFailureEvent(
            ref int eventState,
            int eventId,
            string errorMsg)
        {
            try
            {
                //
                // Only log the first failure after the serial of successful operations, if throttling is turned on.
                //
                if (s_eventLoggerThrottleDisabled ||
                    Interlocked.CompareExchange(
                        ref eventState,
                        EventFailureState,
                        EventSucceedState) == EventSucceedState)
                {
                    if (string.IsNullOrEmpty(errorMsg))
                    {
                        TraceUtility.TraceAssert(Tracing.Source, false, "Error message for storage event log is null or empty.");
                        return;
                    }

                    if (s_eventLoggerActive)
                    {
                        lock (CommonEventLogger.EventLog)
                        {
                            //
                            // Write the error message to event log.
                            //
                            CommonEventLogger.EventLog.WriteEntry(
                                errorMsg,
                                EventLogEntryType.Error,
                                eventId);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_eventLoggerActive = false;
            }
        }

        public static void LogServiceEvent(string eventMessage, int eventId)
        {
            LogServiceEvent(eventMessage, eventId, EventLogEntryType.Information);
        }

        public static void LogServiceEvent(string eventMessage, int eventId, EventLogEntryType eventType)
        {
            try
            {
                if (s_eventLoggerActive)
                {
                    lock (EventLog)
                    {
                        //
                        // Write the success message to event log.
                        //
                        EventLog.WriteEntry(
                            eventMessage,
                            eventType,
                            eventId);
                    }
                }
            }
            catch (Exception ex)
            {
                if (IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_eventLoggerActive = false;
            }
        }

        
        internal static bool IsFatalException(Exception exception)
        {
            return (exception is OutOfMemoryException ||
                 exception is StackOverflowException ||
                 exception is ExecutionEngineException);
        }
    }
}
